package com.clp.protocol.iec104.client.pipeline.state.control;

import com.clp.protocol.iec104.apdu.IApdu;
import com.clp.protocol.iec104.apdu.SApdu;
import com.clp.protocol.iec104.apdu.UApdu;
import com.clp.protocol.iec104.apdu.apci.UCtrlArea;
import com.clp.protocol.iec104.client.async.MasterFuture;
import com.clp.protocol.iec104.client.async.MasterFutureListener;
import com.clp.protocol.iec104.client.async.MasterPromise;
import com.clp.protocol.iec104.client.async.sendapdu.SendStartDtVRes;
import com.clp.protocol.iec104.client.async.sendapdu.SendStopDtVRes;
import com.clp.protocol.iec104.client.config.MasterControlConfig;
import com.clp.protocol.iec104.client.pipeline.MPipelineManager;
import com.clp.protocol.iec104.client.pipeline.state.MasterPromiseRegister;
import com.clp.protocol.core.utils.AssertUtil;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.Nullable;
import java.util.Vector;
import java.util.concurrent.TimeUnit;

/**
 * 启动传输和初始化结束状态处理器，如果没有启动传输或者初始化结束，那么就拒绝任何报文
 */
@Slf4j
public class MDtInitControlStateHandler extends AbstractMControlStateHandler implements MasterPromiseRegister {

    private enum State {
        START_DT_V,
        START_DT_C,
        RECV_INIT_COMPLETED,
        STOP_DT_V,
        STOP_DT_C;
    }

    @Getter
    private final boolean isAutoStartDtV;
    private final int timeoutSeconds;

    private volatile State state = State.STOP_DT_C;
    private volatile long lastStartDtVTime;
    private volatile long lastStartDtCTime;
    private volatile long lastStopDtVTime;
    private volatile long lastStopDtCTime;

    private final Vector<MasterPromise<SendStartDtVRes>> startDtPromises = new Vector<>();
    private final Vector<MasterPromise<SendStopDtVRes>> stopDtPromises = new Vector<>();

    public boolean isStartedDt() {
        return state == State.START_DT_C || state == State.RECV_INIT_COMPLETED;
    }

    public boolean isRecvInitCompleted() {
        return state == State.RECV_INIT_COMPLETED;
    }

    public MDtInitControlStateHandler(MPipelineManager manager, MasterControlConfig cfg) {
        super(manager);
        this.isAutoStartDtV = cfg.isAutoStartDtV();
        this.timeoutSeconds = 5;
    }

    @Override
    protected void resetState() {
        this.state = State.STOP_DT_C;
        this.lastStartDtVTime = System.currentTimeMillis();
        this.lastStartDtCTime = System.currentTimeMillis();
        this.lastStopDtVTime = System.currentTimeMillis();
        this.lastStopDtCTime = System.currentTimeMillis();
    }

    @Override
    protected void afterResetState() {
        // 如果是自动启动传输激活，那么就发送启动传输激活
        if (isAutoStartDtV) {
            uApduSender().sendStartDtV().addListener(new MasterFutureListener<SendStartDtVRes>() {
                @Override
                public void operationComplete(MasterFuture<SendStartDtVRes> future) {
                    if (future.isSuccess()) {
                        log.info("自动启动传输激活成功！");
                    } else {
                        log.info("自动启动传输激活失败！");
                        future.cause().printStackTrace();
                    }
                }
            });
        }
    }

    /**
     * 接收到U帧，应用启动传输过程
     * @param uApdu
     * @return
     */
    @Override
    protected UApdu updateStateByRecv(UApdu uApdu) {
        switch (uApdu.getUApci().getCtrlArea().getUCtrlType()) {
            case U_START_DT_C:
                if (state == State.START_DT_V) {
                    lastStartDtCTime = System.currentTimeMillis();
                    state = State.START_DT_C;
                    startDtPromises.forEach(promise -> {
                        // 接收成功
                        promise.getRes().setRecvAck(true);
                        promise.setSuccess();
                    });
                    startDtPromises.clear();
                }
                break;
            case U_STOP_DT_C:
                if (state == State.STOP_DT_V) {
                    lastStopDtCTime = System.currentTimeMillis();
                    state = State.STOP_DT_C;
                    stopDtPromises.forEach(promise -> {
                        promise.getRes().setRecvAck(true);
                        promise.setSuccess();
                    });
                    stopDtPromises.clear();
                }
                break;
        }
        return uApdu;
    }

    @Override
    protected SApdu updateStateByRecv(SApdu sApdu) {
        return sApdu; // 放行
    }

    @Override
    protected IApdu updateStateByRecv(IApdu iApdu) {
        switch (iApdu.getIAsdu().getTypeTag()) {
            case M_EI_NA_1: // 初始化结束
                switch (iApdu.getIAsdu().getCot().getCause()) {
                    case COT_INIT: // 初始化
                        if (state != State.START_DT_C) {
                            log.warn("初始化结束前不是启动传输确认状态");
                        }
                        state = State.RECV_INIT_COMPLETED;
                        log.info("子站初始化结束，第一次发送总召唤100和总召唤101（电度量召唤");
                        inMaster().getIAsduSender().sendTotalCall100();
                        inMaster().getIAsduSender().sendTotalCall101();
                        break;
                }
                break;
        }
        return iApdu;
    }

    @Override
    protected UApdu updateStateBySend(UApdu uApdu) {
        UCtrlArea ctrlArea = uApdu.getUApci().getCtrlArea();
        switch (ctrlArea.getUCtrlType()) {
            case U_START_DT_V:
                // 第一次发送或重新发送
                if (state == State.STOP_DT_C) {
                    lastStartDtVTime = System.currentTimeMillis();
                    state = State.START_DT_V;
                    // 发送成功
                    startDtPromises.forEach(promise -> promise.getRes().setSendSuccess(true));
                } else {
                    startDtPromises.forEach(promise -> {
                        // 发送失败、接收失败
                        promise.getRes().setSendSuccess(false).setRecvAck(false);
                        promise.setFailure(new RuntimeException("期望状态：" + State.STOP_DT_C + ", 当前状态：" + state));
                    });
                    startDtPromises.clear();
                    uApdu = null;
                }
                break;
            case U_STOP_DT_V:
                if (state == State.START_DT_C) {
                    lastStopDtVTime = System.currentTimeMillis();
                    state = State.STOP_DT_V;
                    // 发送成功
                    stopDtPromises.forEach(promise -> promise.getRes().setSendSuccess(true));
                } else {
                    stopDtPromises.forEach(promise -> {
                        // 发送失败、接收失败
                        promise.getRes().setSendSuccess(false).setRecvAck(false);
                        promise.setFailure(new RuntimeException("期望状态：" + State.START_DT_C + ", 当前状态：" + state));
                    });
                    stopDtPromises.clear();
                    uApdu = null;
                }
                break;
        }
        return uApdu;
    }

    @Override
    protected SApdu updateStateBySend(SApdu sApdu) {
        return sApdu; // 直接放行S帧
    }

    @Override
    protected IApdu updateStateBySend(IApdu iApdu) {
        return iApdu;
    }

    @Override
    @Nullable
    protected ScheduledTask getScheduledTask() {
        return new ScheduledTask(0, 1, TimeUnit.SECONDS) {
            @Override
            public void run() {
                long nowTime = System.currentTimeMillis();
                switch (state) {
                    case START_DT_V: // 发送启动传输激活，进入该状态，处理超时情况
                        if (nowTime - lastStartDtVTime >= timeoutSeconds * 1000L) {
                            startDtPromises.forEach(promise -> {
                                promise.getRes().setRecvAck(false);
                                promise.setFailure(new RuntimeException("Timeout ..."));
                            });
                            startDtPromises.clear();
                            log.error("启动传输激活超时，即将断开连接...");
                            channel.close();
                        }
                        break;
                    case START_DT_C: // 接收到启动传输确认
                        if (nowTime - lastStartDtCTime > 3 * 1000) {
                            log.warn("启动传输后3秒内没有收到初始化结束报文，手动设置初始化结束");
                            state = State.RECV_INIT_COMPLETED;
                            inMaster().getIAsduSender().sendTotalCall100();
                            inMaster().getIAsduSender().sendTotalCall101();
                        }
                        break;
                    case STOP_DT_V: // 发送停止传输激活，处理超时情况
                        if (nowTime - lastStopDtVTime >= timeoutSeconds * 1000L) {
                            stopDtPromises.forEach(promise -> {
                                promise.getRes().setRecvAck(false);
                                promise.setFailure(new RuntimeException("Timeout ..."));
                            });
                            stopDtPromises.clear();
                            log.error("停止传输激活超时，即将断开连接...");
                            channel.close();
                        }
                        break;
                    case STOP_DT_C: // 接收到停止传输确认，无异常情况会发生
                        break;
                }
            }
        };
    }

    @Override
    @SuppressWarnings("unchecked")
    public synchronized <V> void register(MasterPromise<V> sendPromise) {
        V res = sendPromise.getRes();
        AssertUtil.notNull(res);

        if (res instanceof SendStartDtVRes) {
            startDtPromises.add(((MasterPromise<SendStartDtVRes>) sendPromise));
            return;
        }
        if (res instanceof SendStopDtVRes) {
            stopDtPromises.add(((MasterPromise<SendStopDtVRes>) sendPromise));
            return;
        }
    }
}
